package at.ac.tuwien.prog.flash;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import at.ac.tuwien.prog.Util;
import at.ac.tuwien.prog.compiler.ICompiler;
import at.ac.tuwien.prog.entity.Register;
import at.ac.tuwien.prog.entity.Software;
import at.ac.tuwien.prog.memory.MemoryConstants;
import at.ac.tuwien.prog.parser.IParser;

/**
 * creates flash file content.
 * 
 * first register: -8191 (immediate neighbour of program counter)
 * other registers: autoexec application
 * 
 * @author christoph.pickl@student.tuwien.ac.at
 */
public class InitFlashWriter {
	
	private static final Log LOG = LogFactory.getLog(InitFlashWriter.class);

	
	private final ICompiler compiler;
	
	private final IParser parser;
	
	
	public InitFlashWriter(final ICompiler compiler, final IParser parser) {
		this.compiler = compiler;
		this.parser = parser;
	}
	
	
	public byte[] initialFlashData() {
		LOG.info("initialFlashData()");
		
		// full data of flash
		final byte[] data = new byte[FlashManagerImpl.BLOCK_SIZE * FlashManagerImpl.BLOCK_COUNT];
		
		
		// set program counter to immediately execute cmd in next register
		final Register nextPc = Register.newByInt(MemoryConstants.PROGRAM_COUNTER + 1);
		LOG.trace("Wrting at begin of flash block: ProgramCounter="+nextPc.getIntValue()+" (" + nextPc.toBitString() + ")");
		
		// fill first register: copy all 4 bytes (32bits) to block
		final byte[] pcBytes = nextPc.toBytes();
		for (int i = 0; i < 4; i++) {
//			LOG.trace("data["+i+"] = #"+i+": "+ Register.newByInt(pcBytes[i]).toBitString());
			data[i] = pcBytes[i];
		}
		
		

		final Software autoExec = this.interpretStoredSoftwareByAppName("autoexec");
		LOG.trace("interpreted autoexec: " + autoExec);
		
		final byte[] compiledSoftware = this.compiler.compileSoftware(autoExec);
		
		if(compiledSoftware.length > data.length) { // TODO + length of other stuff (PC, ...)
			throw new RuntimeException("Compiled software exceeds flash memory size!");
		}
		
		LOG.trace("autoexec bits: " + Util.toBitString(compiledSoftware));
		// save compiled autoexec binary into  block
		final int compiledSoftwareRegisterCount = compiledSoftware.length / 4;
		for (int i = 0; i < compiledSoftwareRegisterCount; i++) {
			
			final Register writeValue = Register.newBy4SubBytes(compiledSoftware, i * 4);
//			LOG.trace((i+1)+"  "+ writeValue.toBitString());
			
			final byte[] writeValueBytes = writeValue.toBytes();
			for (int j = 0; j < 4; j++) {
				data[i * 4 + j + 4] = writeValueBytes[j]; // "+4" == skip first PC register (32bit/4byte)
			}
			
		}
		
		
//		for (int i = 0; i < compiledSoftware.length; i++) {
//			
//		}
		
		return data;
	}
	
	private Software interpretStoredSoftwareByAppName(final String appName) {
		final URL url = InitFlashWriter.class.getResource("/software/" + appName + ".txt");
		
		File file;
		try {
			file = new File(url.toURI());
		} catch (URISyntaxException e) {
			throw new RuntimeException("Could not read software sourcefile.", e);
		}
		
		try {
			return this.parser.parseFile(file);
		} catch (IOException e) {
			throw new RuntimeException("Could not parse software sourcefile.", e);
		}
	}
	
}
